#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <types.h>
#include <utils.h>
#include <unistd.h>
#include <syserr.h>
#include <sys/epoll.h>
#include <dlist.h>
#include <evtloop.h>
#include <device.h>
#include <async.h>
#include <coroutines.h>
#include <threadpool.h>
#include <softtimer.h>
#include <printk.h>

#define MAX_EP 100

struct _epoll_event
{
    u32         event;
    const char *name;
};
static const struct _epoll_event _epoll_events[] =
{
    {EPOLLIN, "IN"},    {EPOLLOUT, "OUT"},  {EPOLLRDHUP, "RDHUP"},
    {EPOLLPRI, "PRI"},  {EPOLLERR, "ERR"},  {EPOLLHUP, "HUP"},
};
static const char *__used epoll_event_name(u32 event)
{
    int i, idx = 0;
    static char str[0x40];

    str[0] = '\0';
    for (i = 0; i < array_size(_epoll_events); i++)
        if (event & _epoll_events[i].event)
            idx += snprintf(str + idx, sizeof(str) - idx, "%s ", _epoll_events[i].name);
    return str;
}

ev_loop_t *ev_loop_get(const char *name)
{
    return (ev_loop_t *)obj_find_with_type(OBJ_LOOP, name);
}
int ev_loop_register(ev_loop_t *loop, const char *name)
{
    int r;

    r = obj_add(to_obj(loop), OBJ_LOOP, name);
    if (r < 0) return r;

    ev_loop_setup(loop);
    to_obj(loop)->init = 1;
    return 0;
}

void ev_loop_dump(void)
{
    object_t *iter;

    printk("|=====================================|\r\n");
    printk("|           name    |    evfd|    time|\r\n");
    printk("|=====================================|\r\n");

    list_for_each_entry(iter, obj_head(OBJ_LOOP), entry)
    {
        ev_loop_t *loop = (ev_loop_t *)iter;

        printk("|%19s|%8d|%8x|\r\n",
               iter->name, loop->backend_fd, loop->time);
    }
    printk("|=====================================|\r\n");
}

static ev_loop_t *_loop;
static ev_loop_t _ev_default_loop;
ev_loop_t *ev_default_loop(void)
{
    if (!_loop)
    {
        _loop = &_ev_default_loop;
        ev_loop_register(_loop, "loop_dft");
    }
    return _loop;
}

int ev_loop_setup(ev_loop_t *loop)
{
    int ret;

    loop->stop_flag = 0;
    loop->active_handles = 0;
    loop->active_reqs = 0;

    /* epoll setup
     */
    if ((ret = epoll_create(MAX_EP)) < 0)
    {
        fprintf(stderr, "epoll_create fail\n");
        return -1;
    }
    loop->backend_fd = ret;

    /* soft timer setup
     */
    ev_update_time(loop);
    INIT_LIST_HEAD(&loop->timer_head);

    /* async notify setup
     */
    loop->async_io.fd = -1;
    INIT_LIST_HEAD(&loop->async_head);
    svc__async_start(loop);

    /* work queue setup
     */
    INIT_LIST_HEAD(&loop->wq);
    svc_mutex_init(&loop->wq_mutex);
    svc_async_init(loop, &loop->wq_async, svc__work_done, NULL);
    ev__handle_unref(&loop->wq_async);

    /* coroutine setup
     */
    INIT_LIST_HEAD(&loop->cq);
    svc_mutex_init(&loop->cq_mutex);
    svc_async_init(loop, &loop->cq_async, ccb_schedule, NULL);
    ev__handle_unref(&loop->cq_async);
    loop->obj.active = 1;
    return 0;
}

void ev_loop_close(ev_loop_t *loop)
{
    /* coroutine queue close
     */
    svc_async_close(&loop->cq_async);
    svc_mutex_destroy(&loop->cq_mutex);

    /* work queue close
     */
    svc_async_close(&loop->wq_async);
    svc_mutex_destroy(&loop->wq_mutex);

    /* async notify close
     */
    svc__async_stop(loop);
    close(loop->backend_fd);
    loop->backend_fd = -1;

    /* soft timer close
     */
    INIT_LIST_HEAD(&loop->timer_head);

    obj_del(to_obj(loop));
    if (loop == _loop)
        _loop = NULL;

    loop->obj.init = 0;
    loop->obj.active = 0;
}

static int ev_next_timeout(const ev_loop_t *loop)
{
    struct object *obj;
    ev_time_t now = loop->time;
    int to = INT32_MAX;

    if (loop->stop_flag != 0)
        return 0;

    if (!ev__has_active_handles(loop) && !ev__has_active_reqs(loop))
        return 0;

    list_for_each_entry(obj, &loop->timer_head, entry)
    {
        struct soft_timer *t = container_of(obj, struct soft_timer, obj);

        if (ev__is_active(t))
        {
            long timeout = (long)(t->expires - now);
            if (to > timeout)
                to = timeout;
        }
    }
    if (to == INT32_MAX)
        to = -1;
    else if (to > 0)
        to *= (1000 / HZ);
    else
        to = 0;
    return to;
}

void ev_run(ev_loop_t *loop, int timeout)
{
    int i, n;
    struct epoll_event events[MAX_EP];

    n = epoll_wait(loop->backend_fd, events, MAX_EP, timeout);
    for (i = 0; i < n; i++)
    {
        struct epoll_event *e = &events[i];

        ev_io_t *io = e->data.ptr;
        if (io && io->cb)
            io->cb(io, e->events);
    }
}
void ev_stop(ev_loop_t *loop)
{
    loop->stop_flag = 1;
}

static int ev__loop_alive(const ev_loop_t *loop)
{
    return ev__has_active_handles(loop) ||
           ev__has_active_reqs(loop);
}
void ev_auto_run(ev_loop_t *loop, int mode)
{
    int to;

    assert(loop->obj.init == 1);
    while (ev__loop_alive(loop) && loop->stop_flag == 0)
    {
        to = 0;

        ev_update_time(loop);
        soft_timer_service_run(loop);

        if (mode == EV_RUN_ONCE || mode == EV_RUN_DEFAULT)
            to = ev_next_timeout(loop);

        ev_run(loop, to);

        if (mode == EV_RUN_ONCE)
        {
            ev_update_time(loop);
            soft_timer_service_run(loop);
        }

        if (mode == EV_RUN_ONCE || mode == EV_RUN_NOWAIT)
            break;
    }

    /* The if statement lets gcc compile it to a conditional store. Avoids
     * dirtying a cache line.
     */
    if (loop->stop_flag != 0)
        loop->stop_flag = 0;
}

void ev_update_time(ev_loop_t *loop)
{
    loop->time = hw_time();
}
ev_time_t ev_now(const ev_loop_t *loop)
{
    return loop->time;
}

static void ev_epoll_ctl(ev_loop_t *loop, ev_io_t *io)
{
    struct epoll_event e;
    int op = io->events == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;

    e.events   = io->pevents/* | EPOLLET*/;
    e.data.ptr = io;

    if (io->pevents == 0)
    {
        epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, io->fd, NULL);
    }
    else
    {
        if (epoll_ctl(loop->backend_fd, op, io->fd, &e))
        {
            if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, io->fd, &e))
                perror("add event fault");
        }
    }

    io->events = io->pevents;
}

int ev_is_active(const void *obj)
{
    return ev__is_active(obj);
}

void ev__io_init(ev_io_t *io, io_cb_t cb, int fd)
{
    memset(io, 0, sizeof(*io));
    io->fd   = fd;
    io->cb   = cb;
}

void ev__io_start(ev_loop_t *loop, ev_io_t *io, u32 events)
{
    io->pevents |= events;

    if (io->events == io->pevents)
        return;

    ev_epoll_ctl(loop, io);
}

void ev__io_stop(ev_loop_t *loop, ev_io_t *io, u32 events)
{
    io->pevents &= ~events;

    if (io->events == io->pevents)
        return;

    ev_epoll_ctl(loop, io);
}

#ifdef __GNUC__  /* Also covers __clang__ and __INTEL_COMPILER. */
__attribute__((destructor))
#endif
void uv_library_shutdown(void)
{
    static int was_shutdown;

    if (was_shutdown)
        return;

    uv__threadpool_cleanup();
    was_shutdown = 1;
}
